package ner;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CharacterDetailsExtractor {
	

	private static final int VERB_GENDER_WEIGHT=2;
	private static final int ADJECTIVE_GENDER_WEIGHT=1;
				
	
	public void findPositivesNegativesAndDescs(ExtractionTask extractionTask){

		Map<Integer, List<BookCharacter>> sentencesWithCharacters=extractionTask.getSentencesWithCharacters();
		
	
		for(Map.Entry<Integer, List<BookCharacter>> entry:sentencesWithCharacters.entrySet()){
			
			
			int numberOfCharacters=entry.getValue().size();
			
			if(numberOfCharacters==1){
			
			for(BookCharacter bch:entry.getValue()){
				
				
					Integer sentenceNumber=entry.getKey();
					String sentence=extractionTask.getSentences().get(sentenceNumber);
					List<String> words=Segmenter.splitByWords(sentence);
					List<Integer> indexesOfBchOccurrence=RecognitionUtil.findBchInSentenceAll(bch,words);
					
					int index=0;
					for(String w:words){
						
						PartOfSpeech pos=POStagger.getPOS(w);
						Pair<GrammaticalGender,GrammaticalNumber> gn= POStagger.getGrammaticalGenderAndNumber(w);
						String baseWord=extractionTask.getBaseWords().get(sentenceNumber).get(index);
						int distance=findDistanceToClosest(index,indexesOfBchOccurrence);
						int contextValue;
						if(Knowledge.postiveWords.contains(baseWord)){
							
							if((contextValue=estimateContextValue(numberOfCharacters,gn,bch,distance,words.size(),pos))!=0)
								bch.getPositiveSentences().add(sentenceNumber);
							
						}
						if(Knowledge.negativeWords.contains(baseWord)){
							if((contextValue=estimateContextValue(numberOfCharacters,gn,bch,distance,words.size(),pos))!=0)
								bch.getNegativeSentences().add(sentenceNumber);			
							
						}
						
						if(Knowledge.lookDescWords.contains(baseWord)){
							
							if((contextValue=estimateContextValue(numberOfCharacters,gn,bch,distance,words.size(),pos))!=0)
								bch.getLookDescriptionSentences().add(sentenceNumber);
							
						}
						if(Knowledge.generalDescWords.contains(baseWord)){
							if((contextValue=estimateContextValue(numberOfCharacters,gn,bch,distance,words.size(),pos))!=0)
								bch.getGeneralDescriptionSentences().add(sentenceNumber);			
							
						}
						
						
						index++;
					}
	
		
				
			}
			}
		
		}
	
	}
	
	public void assessBookCharacters(ExtractionTask task){
		
		
		for(BookCharacter bch:task.getCharacters()){
			
			int posAndNegsSum=bch.getPositiveSentences().size()+bch.getNegativeSentences().size();
			if(posAndNegsSum==0){
				bch.setPositiveness(0.0);
			}
			else{
				int goodVsBad=bch.getPositiveSentences().size()-bch.getNegativeSentences().size();
			//	System.out.println(goodVsBad+","+posAndNegsSum);
				
				bch.setPositiveness(0.0+(((double)goodVsBad/(double)posAndNegsSum)*100.0));
			}	


			
		}
		
		
		
		
	}
	
	
	public void findSynonyms(ExtractionTask extractionTask) {
		
		Map<Integer, List<BookCharacter>> sentencesWithCharacters=extractionTask.getSentencesWithCharacters();
		
		
		for(Map.Entry<Integer, List<BookCharacter>> entry:sentencesWithCharacters.entrySet()){
			
			
			int numberOfCharacters=entry.getValue().size();
			
		
			for(BookCharacter bch:entry.getValue()){
				
				
					Integer sentenceNumber=entry.getKey();
					String sentence=extractionTask.getSentences().get(sentenceNumber);
					List<String> words=Segmenter.splitByWords(sentence);
					List<Integer> indexesOfBchOccurrence=RecognitionUtil.findBchInSentenceAll(bch,words);
					
					int index=0;
					for(String w:words){
						
						PartOfSpeech pos=POStagger.getPOS(w);
						if(pos!=null && pos.equals(PartOfSpeech.NOUN)){
							Pair<GrammaticalGender,GrammaticalNumber> gn= POStagger.getGrammaticalGenderAndNumber(w);
							String baseWord=extractionTask.getBaseWords().get(sentenceNumber).get(index);
							int contextValue;
							if((contextValue=estimateContextValue(numberOfCharacters,gn,bch,1,words.size(),pos))!=0){
								Integer value=null;
								if((value=bch.getSynonymCandidates().get(baseWord))!=null){
									bch.getSynonymCandidates().put(baseWord,value+1);
								}
								else{
									bch.getSynonymCandidates().put(baseWord,1);
								}
									
							
							}
			
						}
						
						index++;
					}
	
		
				
			
			}
		}
		
	}
		
	
	
	private int findDistanceToClosest(int index,List<Integer> indexesOfBchOccurrence) {
		
		if(indexesOfBchOccurrence.size()==0)
			return -1;
		else{
			int min=index-indexesOfBchOccurrence.get(0);
			for(int i=1;i<indexesOfBchOccurrence.size();i++){
				
				if((Math.abs(index-indexesOfBchOccurrence.get(i)))<Math.abs(min))
					min=index-indexesOfBchOccurrence.get(i);
			}
			return min;
		}
		
	}


	public void setGenderAndNumber(ExtractionTask extractionTask){
		
		Map<Integer, List<BookCharacter>> sentencesWithCharacters=extractionTask.getSentencesWithCharacters();
		Map<BookCharacter, Map<GrammaticalGender,Integer>> bchGGOccurrences=new HashMap<BookCharacter,Map<GrammaticalGender,Integer>>();	
		Map<BookCharacter, Integer> bchIsSingular=new HashMap<BookCharacter,Integer>();		//positive integer indicates singular, negative plural
		
		for(Map.Entry<Integer, List<BookCharacter>> entry:sentencesWithCharacters.entrySet()){
			
			int numberOfCharacters=entry.getValue().size();

		
			if(numberOfCharacters==1){															//sentence with many book characters are difficult to interpret in this case
	
				String sentence=extractionTask.getSentences().get(entry.getKey());
				List<String> words=Segmenter.splitByWords(sentence);
				
				for(BookCharacter bch:entry.getValue()){
					
					int indexOfBchOccurrence=RecognitionUtil.findBchInSentence(bch,words);
					
					Map<Integer,String> verbs=findPOSInSentenceWithRelativeDistances(words,PartOfSpeech.VERB,indexOfBchOccurrence);
					Map<Integer,String> adjectives=findPOSInSentenceWithRelativeDistances(words,PartOfSpeech.ADJECTIVE,indexOfBchOccurrence);
					
					Map<GrammaticalGender,Integer> ggOccurrences=new HashMap<GrammaticalGender, Integer>();
					
					int indicationOfSingularity=0;
					
					for(String word:verbs.values()){			//TODO distance not considered temporarily
						
						Pair<GrammaticalGender,GrammaticalNumber> genderAndNumber=POStagger.getGrammaticalGenderAndNumber(word);
						GrammaticalGender gg=genderAndNumber.getFirst();
						GrammaticalNumber gn=genderAndNumber.getSecond();
						
							
							
					//	System.out.println("word:"+word+" gramm gender: "+gg);
						
						if(gg!=null){
							Integer value;
							if((value=ggOccurrences.get(gg))!=null)
								ggOccurrences.put(gg, value+VERB_GENDER_WEIGHT);
							else
								ggOccurrences.put(gg, VERB_GENDER_WEIGHT);
						}
						if(gn!=null){
							
							if(gn.equals(GrammaticalNumber.SINGULAR))
								indicationOfSingularity++;
							else
								indicationOfSingularity--;
							
						}
							
						
						
					}
					
					for(Map.Entry<Integer, String> distAndWord:adjectives.entrySet()){			///TODO merege with  branch above
						
						Pair<GrammaticalGender,GrammaticalNumber> genderAndNumber=POStagger.getGrammaticalGenderAndNumber(distAndWord.getValue());
						GrammaticalGender gg=genderAndNumber.getFirst();
						GrammaticalNumber gn=genderAndNumber.getSecond();
						
					//	if(bch.getName().toLowerCase().equals("dinah") && (gg.equals(GrammaticalGender.NONMASCULINE_PLURAL) ||gg.equals(GrammaticalGender.MASCULINE_PLURAL)) ){
					//		System.out.println("PLURAL : "+distAndWord.getValue());
							
					//	}
			
					//	System.out.println("word:"+word+" gramm gender: "+gg);
						
						if(gg!=null){
							Integer value;
							if((value=ggOccurrences.get(gg))!=null)
								ggOccurrences.put(gg, value+adjDistanceToGenderWeight(distAndWord.getKey()));
							else
								ggOccurrences.put(gg, adjDistanceToGenderWeight(distAndWord.getKey()));
						}
						if(gn!=null){
							
							if(gn.equals(GrammaticalNumber.SINGULAR))
								indicationOfSingularity+=adjDistanceToNumberWeight(distAndWord.getKey());
							else
								indicationOfSingularity-=adjDistanceToNumberWeight(distAndWord.getKey());
							
						}
						
					}
					if(ggOccurrences!=null && ggOccurrences.size()>1){
						if(bchGGOccurrences.get(bch)==null)
							bchGGOccurrences.put(bch,ggOccurrences);
						else{
							bchGGOccurrences.put(bch,Util.addMapsWithOccurrences(bchGGOccurrences.get(bch), ggOccurrences));
						}
					}
					if(bchIsSingular.get(bch)==null)
						bchIsSingular.put(bch, indicationOfSingularity);
					else
						bchIsSingular.put(bch,bchIsSingular.get(bch)+indicationOfSingularity);
					
					
				}
			}
			
		}
		
		for(BookCharacter bch:extractionTask.getCharacters()){	//we determine number for every bookcharacter, if we don't have information we guess it is singular
			Integer indicationOfSingularity=bchIsSingular.get(bch);
			if(indicationOfSingularity==null){

				indicationOfSingularity=0;
				
			}

			
			if(indicationOfSingularity<0){
				bch.setNumber(GrammaticalNumber.PLURAL);
				if(bchGGOccurrences.get(bch)!=null){									//it is plural so we should exclude this possibilities
					bchGGOccurrences.get(bch).remove(GrammaticalGender.MASCULINE);
					bchGGOccurrences.get(bch).remove(GrammaticalGender.FEMININE);
					bchGGOccurrences.get(bch).remove(GrammaticalGender.NEUTER);
					bchGGOccurrences.get(bch).remove(GrammaticalGender.UKNOWN_SINGULAR);
				}
			}	
			else{
				bch.setNumber(GrammaticalNumber.SINGULAR);
				if(bchGGOccurrences.get(bch)!=null){	
					bchGGOccurrences.get(bch).remove(GrammaticalGender.NONMASCULINE_PLURAL);
					bchGGOccurrences.get(bch).remove(GrammaticalGender.MASCULINE_PLURAL);
					bchGGOccurrences.get(bch).remove(GrammaticalGender.UNKNOWN_PLURAL);
				}
			}
		}
		

		for(Map.Entry<BookCharacter, Map<GrammaticalGender,Integer>> entry:bchGGOccurrences.entrySet()){
/*			if(entry.getKey().getName().toLowerCase().equals("dinah")){
				System.out.println("NEL!!!");
				for(Map.Entry<GrammaticalGender, Integer> entry2:entry.getValue().entrySet()){
					System.out.println(entry2.getKey()+":"+entry2.getValue());
					
				}
				
			}
		*/	
			GrammaticalGender mostFrequentGrammGender=Util.getMostFrequentFromMap(entry.getValue());
			entry.getKey().setGender(GrammaticalGender.convertToNormalGender(mostFrequentGrammGender));
			
		}
		
		
	}
	
	
	private Integer adjDistanceToGenderWeight(Integer distance) {
															///maybe take length of sentence into acccount
		if(distance==-1)
			return 100*VERB_GENDER_WEIGHT;
		else if(distance==-2)
			return 25*VERB_GENDER_WEIGHT;
		else if(Math.abs(distance)<=3)
			return 2*ADJECTIVE_GENDER_WEIGHT;
		
		else{
			
			return ADJECTIVE_GENDER_WEIGHT;
			
		}
			
			
	}
	
	private Integer adjDistanceToNumberWeight(Integer distance) {
		///maybe take length of sentence into acccount
		if(distance==-1)
			return 50;
		else if(distance==-2)
			return 2;
		else if(Math.abs(distance)<=3)
			return 2;
		
		else{
		
			return 1;

		}

	}

	private Map<Integer,String> findPOSInSentenceWithIndexes(List<String> words,PartOfSpeech partOfSpeech){
		
		Map<Integer,String> wordsSelected=new HashMap<Integer,String>();
		int index=0;
		for(String s:words){
			
			PartOfSpeech pos=POStagger.getPOS(s);
			if(pos!=null && pos.equals(partOfSpeech)){
				wordsSelected.put(index,s);
			}
			index++;
		}
	
		return wordsSelected;
		
	}
	
	private Map<Integer,String> findPOSInSentenceWithRelativeDistances(List<String> words,PartOfSpeech partOfSpeech, int targetIndex){
		
		Map<Integer,String> wordsAndRelativeDistances=new HashMap<Integer,String>();
		
		for(Map.Entry<Integer, String> entry:findPOSInSentenceWithIndexes(words,partOfSpeech).entrySet()){
			
			wordsAndRelativeDistances.put(entry.getKey()-targetIndex,entry.getValue());
			
		}
	
		return wordsAndRelativeDistances;
		
	}
	
	
	
	private int estimateContextValue(int numberOfCharacters, Pair<GrammaticalGender, GrammaticalNumber> descWordGN,BookCharacter  bch, int distance, int sentenceLength, PartOfSpeech pos){
		
		//disqualifiers
		if(numberOfCharacters>1)
			return 0;
		Gender  descWordGender=GrammaticalGender.convertToNormalGender(descWordGN.getFirst());
		if(!(descWordGender==null || bch.getGender()==null ||
				descWordGender.equals(bch.getGender())))
			return 0;
		if(!(descWordGN.getSecond()==null || bch.getNumber()==null ||
				descWordGN.getSecond().equals(bch.getNumber())))
			return 0;
		
		
		//details
		if(distance==-1 && pos.equals(PartOfSpeech.ADJECTIVE))
			return 10;
		else if(distance==1 && pos.equals(PartOfSpeech.VERB))
			return 10;
		else if(Math.abs(distance)>1 && Math.abs(distance)<=3)
			return 5;
		else 
			return 1;
			
		
		
		
		
	}
	
	
	
	
	
	
	
	
	
	
	private void testSentencesWithCharacters(ExtractionTask extractionTask){
		
		Map<Integer, List<BookCharacter>> sentencesWithCharacters=extractionTask.getSentencesWithCharacters();
	
		int counter0=0;
		for(List<BookCharacter> list:sentencesWithCharacters.values() ){
			
			counter0+=list.size();
		}
		System.out.println("SENTENCE COUNTER1: "+counter0);
		int counter=0;
		for(BookCharacter bch:extractionTask.getCharacters()){	
			for(Integer i:bch.getSentencesNumbers()){
				counter++;
			}
		}
		System.out.println("SENTENCE COUNTER2: "+counter);
	}
	

}
